// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Micro-benchmark for ffi struct field stores and loads.
//
// Only tests a single field because the FfiMemory benchmark already tests loads
// and stores of different field sizes.

import 'dart:ffi';

import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:ffi/ffi.dart';

//
// Struct field store (plus Pointer elementAt and load).
//

void doStoreInt32(Pointer<VeryLargeStruct> pointer, int length) {
  for (int i = 0; i < length; i++) {
    pointer[i].c = 1;
  }
}

//
// Struct field load (plus Pointer elementAt and load).
//

int doLoadInt32(Pointer<VeryLargeStruct> pointer, int length) {
  int x = 0;
  for (int i = 0; i < length; i++) {
    x += pointer[i].c;
  }
  return x;
}

//
// Benchmark fixture.
//

// Number of repeats: 1000
//  * CPU: Intel(R) Xeon(R) Gold 6154
//    * Architecture: x64
//      * 150000 - 465000 us (without optimizations)
//      * 14 - ??? us (expected with optimizations, on par with typed data)
const N = 1000;

class FieldLoadStore extends BenchmarkBase {
  Pointer<VeryLargeStruct> pointer = nullptr;
  FieldLoadStore() : super('FfiStruct.FieldLoadStore');

  @override
  void setup() => pointer = calloc(N);
  @override
  void teardown() => calloc.free(pointer);

  @override
  void run() {
    doStoreInt32(pointer, N);
    final int x = doLoadInt32(pointer, N);
    if (x != N) {
      throw Exception('$name: Unexpected result: $x');
    }
  }
}

//
// Main driver.
//

void main(List<String> args) {
  final benchmarks = [
    FieldLoadStore.new,
  ];

  final filter = args.firstOrNull;
  for (var constructor in benchmarks) {
    final benchmark = constructor();
    if (filter == null || benchmark.name.contains(filter)) {
      benchmark.report();
    }
  }
}

//
// Test struct.
//
final class VeryLargeStruct extends Struct {
  @Int8()
  external int a;

  @Int16()
  external int b;

  @Int32()
  external int c;

  @Int64()
  external int d;

  @Uint8()
  external int e;

  @Uint16()
  external int f;

  @Uint32()
  external int g;

  @Uint64()
  external int h;

  @IntPtr()
  external int i;

  @Double()
  external double j;

  @Float()
  external double k;

  external Pointer<VeryLargeStruct> parent;

  @IntPtr()
  external int numChildren;

  external Pointer<VeryLargeStruct> children;

  @Int8()
  external int smallLastField;
}
